/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package central.server;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;
import share.FileCommunicator;
import share.Server;
import share.SocketCommunicator;

/**
 *
 * @author mire
 */
public class CentralOperator {

    private String name;
    private ConcurrentHashMap<Integer, String> users;   //ID + number
    private ConcurrentHashMap<Integer, Server> servers;
    private ConcurrentHashMap<Integer, CentralServerListener> serverListeners;
    private ScheduledExecutorService aliveCheckThread = null;
    private String backupUsersName;
    private String backupServersName;
    private boolean updated;

    public CentralOperator(String name) {
        this.name = name;
        users = new ConcurrentHashMap<Integer, String>();
        servers = new ConcurrentHashMap<Integer, Server>();
        serverListeners = new ConcurrentHashMap<Integer, CentralServerListener>();
        backupUsersName = "." + name + ".users";
        backupServersName = "." + name + ".servers";
        updated = false;
    }

    public String getName() {
        return name;
    }

    public ConcurrentHashMap<Integer, Server> getServers() {
        return servers;
    }

    public ConcurrentHashMap<Integer, CentralServerListener> getServerListeners() {
        return serverListeners;
    }

    public ConcurrentHashMap<Integer, String> getUsers() {
        return users;
    }

    public String getBackupServersName() {
        return backupServersName;
    }

    public String getBackupUsersName() {
        return backupUsersName;
    }

    public ScheduledExecutorService getAliveCheckThread() {
        return aliveCheckThread;
    }

    public void setAliveCheckThread(ScheduledExecutorService aliveCheckThread) {
        this.aliveCheckThread = aliveCheckThread;
    }

    public boolean isUpdated() {
        return updated;
    }

    public void setUpdated(boolean updated) {
        this.updated = updated;
    }

    public ConcurrentHashMap<Integer, String> parseUsers(String manyUsers) {
        ConcurrentHashMap<Integer, String> usersList = new ConcurrentHashMap<Integer, String>();
        String workingString = manyUsers.replace('{', ' ').replace('}', ' ').replaceAll(" ", "");
        String[] splited = workingString.split(",");
        for (int i = 0; i < splited.length; i++) {
            String[] userSplited = splited[i].split("=");
            usersList.put(Integer.parseInt(userSplited[0]), userSplited[1]);
        }
        return usersList;
    }

    public synchronized void backupUsersAsObjects() {
        File f = new File(backupUsersName);
        if (f.exists()) {
            f.delete();
        }

        FileCommunicator fc = new FileCommunicator(f);
        try {
            fc.open();
            fc.writeObject(users);
            fc.close();
        } catch (IOException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public synchronized void backupUsers() {
        File f = new File(backupUsersName);
        if (f.exists()) {
            f.delete();
        }

        FileWriter fw = null;
        PrintWriter outp = null;
        try {
            fw = new FileWriter(f);
            outp = new PrintWriter(fw, true);

            outp.println(users.toString());

        } catch (IOException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (outp != null) {
                    outp.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public synchronized boolean restoreUsersAsObjects() {
        File f = new File(backupUsersName);
        if (!f.exists()) {
            return false;
        }

        FileCommunicator fc = new FileCommunicator(f);
        try {
            fc.open();
            users = (ConcurrentHashMap<Integer, String>) fc.readObject();
            fc.close();
        } catch (IOException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        }
        if (users.isEmpty()) {
            return false;
        }

        return true;
    }

    public synchronized boolean restoreUsers() {
        File f = new File(backupUsersName);
        if (!f.exists()) {
            return false;
        }

        FileReader fr = null;
        LineNumberReader inr = null;
        String line = null;
        try {
            fr = new FileReader(f);
            inr = new LineNumberReader(fr);

            line = inr.readLine();

        } catch (IOException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (inr != null) {
                    inr.close();
                }
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        if ((line != null) && (!line.equals("{}"))) {
            users = parseUsers(line);
        } else {
            return false;
        }
        return true;
    }

    public synchronized void backupServersAsObjects() {
        File f = new File(backupServersName);
        if (f.exists()) {
            f.delete();
        }
        FileCommunicator fc = new FileCommunicator(f);
        try {
            fc.open();
            fc.writeObject(servers);
            fc.close();
        } catch (IOException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public synchronized void backupServers() {
        File f = new File(backupServersName);
        if (f.exists()) {
            f.delete();
        }
        FileWriter fw = null;
        PrintWriter outp = null;
        try {
            fw = new FileWriter(f);
            outp = new PrintWriter(fw, true);

            for (Iterator<Server> it = servers.values().iterator(); it.hasNext();) {
                Server server = it.next();

                outp.println(server.getAddress() + " " + server.getThreadNo() + " " + server.getCentralPort());
            }

        } catch (IOException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (outp != null) {
                    outp.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public synchronized boolean restoreServersAsObjects() {
        File f = new File(backupServersName);
        if (!f.exists()) {
            return false;
        }

        FileCommunicator fc = new FileCommunicator(f);
        try {
            fc.open();
            servers = (ConcurrentHashMap<Integer, Server>) fc.readObject();
            fc.close();
        } catch (IOException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        }
        if (servers.isEmpty()) {
            return false;
        }

        return true;
    }

    public synchronized boolean restoreServers() {
        File f = new File(backupServersName);
        if (!f.exists()) {
            return false;
        }

        FileReader fr = null;
        LineNumberReader inr = null;
        String line = null;
        try {
            fr = new FileReader(f);
            inr = new LineNumberReader(fr);

            while ((line = inr.readLine()) != null) {
                String[] splited = line.split(" ");
                Server newServer = new Server(splited[0], Integer.parseInt(splited[1]), Integer.parseInt(splited[2]));
                servers.put(newServer.hashCode(), newServer);
            }

        } catch (IOException ex) {
            //kraj fajla
            Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                if (inr != null) {
                    inr.close();
                }
                if (fr != null) {
                    fr.close();
                }
            } catch (IOException ex) {
                Logger.getLogger(CentralOperator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return true;
    }

    public boolean update(CentralServer centralServer) {
        updated = false;
        ConcurrentHashMap<Integer, Server> newServers;
        Iterator<Server> itServ = servers.values().iterator();
        while (itServ.hasNext() && !isUpdated()) {
            Server server = itServ.next();
            //update
            SocketCommunicator socComm = null;
            try {
                socComm = new SocketCommunicator(new Socket(server.getAddress(), server.getCentralPort()));
                socComm.open();
                socComm.writeString("UPDATE_OPERATOR");
                socComm.readString(); //REQUEST_ACCEPTED
                if (!socComm.readString().equals("NO_NEW_USERS")) {
                    String usersString = socComm.readString();
                    ConcurrentHashMap<Integer, String> usersList = parseUsers(usersString);
                    users.putAll(usersList);
                    backupUsers();
                }

                newServers = (ConcurrentHashMap<Integer, Server>) socComm.readObject();

                servers.putAll(newServers);
                for (Iterator<Server> it = newServers.values().iterator(); it.hasNext();) {
                    Server s = it.next();
                    centralServer.startListener(name, s);
                }
                backupServers();
                updated = true;
                socComm.writeString("OK");
                socComm.close();

            } catch (ClassNotFoundException ex) {
                Logger.getLogger(CentralServer.class.getName()).log(Level.SEVERE, null, ex);
            } catch (UnknownHostException ex) {
                Logger.getLogger(CentralServer.class.getName()).log(Level.SEVERE, null, ex);
                server.setOnline(false);
                centralServer.stopListener(name, server);
            } catch (IOException ex) {
                Logger.getLogger(CentralServer.class.getName()).log(Level.SEVERE, null, ex);
                server.setOnline(false);
                centralServer.stopListener(name, server);
            } finally {
                if (socComm != null) {
                    try {
                        socComm.close();
                    } catch (IOException ex) {
                        Logger.getLogger(CentralServer.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
        return updated;
    }

    public void updateServerStatus(String address) {
        for (Iterator<Server> it = servers.values().iterator(); it.hasNext();) {
            Server server = it.next();
            if (server.getAddress().equals(address)) {
                server.setOnline(true);
            }
        }
    }

    @Override
    public String toString() {
        return name;
    }
}
